home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-19 / rcs55.zip / RCSUTIL.C < prev    next >
C/C++ Source or Header  |  1991-09-15  |  19KB  |  798 lines

  1. /*
  2.  *                     RCS utilities
  3.  */
  4.  
  5. /* Copyright (C) 1982, 1988, 1989 Walter Tichy
  6.    Copyright 1990 by Paul Eggert
  7.    Distributed under license by the Free Software Foundation, Inc.
  8.  
  9. This file is part of RCS.
  10.  
  11. RCS is free software; you can redistribute it and/or modify
  12. it under the terms of the GNU General Public License as published by
  13. the Free Software Foundation; either version 1, or (at your option)
  14. any later version.
  15.  
  16. RCS is distributed in the hope that it will be useful,
  17. but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19. GNU General Public License for more details.
  20.  
  21. You should have received a copy of the GNU General Public License
  22. along with RCS; see the file COPYING.  If not, write to
  23. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  24.  
  25. Report problems and direct all questions to:
  26.  
  27.     rcs-bugs@cs.purdue.edu
  28.  
  29. */
  30.  
  31.  
  32.  
  33.  
  34. /* $Log: rcsutil.c%v $
  35.  * Revision 1.2  1991/08/23  13:38:09  SGP
  36.  * Ported to MSDOS using Borland C++
  37.  *
  38.  * Revision 5.5  1990/12/04  05:18:49  eggert
  39.  * Don't output a blank line after a signal diagnostic.
  40.  * Use -I for prompts and -q for diagnostics.
  41.  *
  42.  * Revision 5.4  1990/11/01  05:03:53  eggert
  43.  * Remove unneeded setid check.  Add awrite(), fremember().
  44.  *
  45.  * Revision 5.3  1990/10/06  00:16:45  eggert
  46.  * Don't fread F if feof(F).
  47.  *
  48.  * Revision 5.2  1990/09/04  08:02:31  eggert
  49.  * Store fread()'s result in an fread_type object.
  50.  *
  51.  * Revision 5.1  1990/08/29  07:14:07  eggert
  52.  * Declare getpwuid() more carefully.
  53.  *
  54.  * Revision 5.0  1990/08/22  08:13:46  eggert
  55.  * Add setuid support.  Permit multiple locks per user.
  56.  * Remove compile-time limits; use malloc instead.
  57.  * Switch to GMT.  Permit dates past 1999/12/31.
  58.  * Add -V.  Remove snooping.  Ansify and Posixate.
  59.  * Tune.  Some USG hosts define NSIG but not sys_siglist.
  60.  * Don't run /bin/sh if it's hopeless.
  61.  * Don't leave garbage behind if the output is an empty pipe.
  62.  * Clean up after SIGXCPU or SIGXFSZ.  Print name of signal that caused cleanup.
  63.  *
  64.  * Revision 4.6  89/05/01  15:13:40  narten
  65.  * changed copyright header to reflect current distribution rules
  66.  * 
  67.  * Revision 4.5  88/11/08  16:01:02  narten
  68.  * corrected use of varargs routines
  69.  * 
  70.  * Revision 4.4  88/08/09  19:13:24  eggert
  71.  * Check for memory exhaustion.
  72.  * Permit signal handlers to yield either 'void' or 'int'; fix oldSIGINT botch.
  73.  * Use execv(), not system(); yield exit status like diff(1)'s.
  74.  * 
  75.  * Revision 4.3  87/10/18  10:40:22  narten
  76.  * Updating version numbers. Changes relative to 1.1 actually
  77.  * relative to 4.1
  78.  * 
  79.  * Revision 1.3  87/09/24  14:01:01  narten
  80.  * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  81.  * warnings)
  82.  * 
  83.  * Revision 1.2  87/03/27  14:22:43  jenkins
  84.  * Port to suns
  85.  * 
  86.  * Revision 4.1  83/05/10  15:53:13  wft
  87.  * Added getcaller() and findlock().
  88.  * Changed catchints() to check SIGINT for SIG_IGN before setting up the signal
  89.  * (needed for background jobs in older shells). Added restoreints().
  90.  * Removed printing of full RCS path from logcommand().
  91.  * 
  92.  * Revision 3.8  83/02/15  15:41:49  wft
  93.  * Added routine fastcopy() to copy remainder of a file in blocks.
  94.  *
  95.  * Revision 3.7  82/12/24  15:25:19  wft
  96.  * added catchints(), ignoreints() for catching and ingnoring interrupts;
  97.  * fixed catchsig().
  98.  *
  99.  * Revision 3.6  82/12/08  21:52:05  wft
  100.  * Using DATEFORM to format dates.
  101.  *
  102.  * Revision 3.5  82/12/04  18:20:49  wft
  103.  * Replaced SNOOPDIR with SNOOPFILE; changed addlock() to update
  104.  * lockedby-field.
  105.  *
  106.  * Revision 3.4  82/12/03  17:17:43  wft
  107.  * Added check to addlock() ensuring only one lock per person.
  108.  * Addlock also returns a pointer to the lock created. Deleted fancydate().
  109.  *
  110.  * Revision 3.3  82/11/27  12:24:37  wft
  111.  * moved rmsema(), trysema(), trydiraccess(), getfullRCSname() to rcsfnms.c.
  112.  * Introduced macro SNOOP so that snoop can be placed in directory other than
  113.  * TARGETDIR. Changed %02d to %.2d for compatibility reasons.
  114.  *
  115.  * Revision 3.2  82/10/18  21:15:11  wft
  116.  * added function getfullRCSname().
  117.  *
  118.  * Revision 3.1  82/10/13  16:17:37  wft
  119.  * Cleanup message is now suppressed in quiet mode.
  120.  */
  121.  
  122.  
  123.  
  124.  
  125. #include "rcsbase.h"
  126.  
  127. #if !MAKEDEPEND && defined(declare_getpwuid)
  128. #    include <pwd.h>
  129.     declare_getpwuid
  130. #endif
  131.  
  132. libId(utilId, "$Id: rcsutil.c%v 1.2 1991/08/23 13:38:09 SGP Exp $")
  133.  
  134. #if lint
  135.     malloc_type lintalloc;
  136. #endif
  137.  
  138. #if has_getuid
  139.     uid_t ruid;
  140. #endif
  141. #if SETID
  142.     static uid_t euid;
  143.     static gid_t egid, rgid;
  144. #endif
  145.  
  146. /*
  147.  * list of blocks allocated with ftestalloc()
  148.  * These blocks can be freed by ffree when we're done with the current file.
  149.  * We could put the free block inside struct alloclist, rather than a pointer
  150.  * to the free block, but that would be less portable.
  151.  */
  152. struct alloclist {
  153.     malloc_type alloc;
  154.     struct alloclist *nextalloc;
  155. };
  156. static struct alloclist *alloced;
  157.  
  158.  
  159.     static malloc_type
  160. okalloc(malloc_type p)
  161. {
  162.     if (!p)
  163.         faterror("out of memory");
  164.     return p;
  165. }
  166.  
  167.     malloc_type
  168. testalloc(size_t size)
  169. /* Allocate a block, testing that the allocation succeeded.  */
  170. {
  171.     return okalloc(malloc(size));
  172. }
  173.  
  174.     malloc_type
  175. testrealloc(malloc_type ptr, size_t size)
  176. /* Reallocate a block, testing that the allocation succeeded.  */
  177. {
  178.     return okalloc(realloc(ptr, size));
  179. }
  180.  
  181.     malloc_type
  182. fremember(malloc_type ptr)
  183. /* Remember PTR in 'alloced' so that it can be freed later.  Yield PTR.  */
  184. {
  185.     register struct alloclist *q = talloc(struct alloclist);
  186.     q->nextalloc = alloced;
  187.     alloced = q;
  188.     return q->alloc = ptr;
  189. }
  190.  
  191.     malloc_type
  192. ftestalloc(size_t size)
  193. /* Allocate a block, putting it in 'alloced' so it can be freed later. */
  194. {
  195.     return fremember(testalloc(size));
  196. }
  197.  
  198.     void
  199. ffree()
  200. /* Free all blocks allocated with ftestalloc().  */
  201. {
  202.     register struct alloclist *p, *q;
  203.     for (p = alloced;  p;  p = q) {
  204.         q = p->nextalloc;
  205.         tfree(p->alloc);
  206.         tfree(p);
  207.     }
  208.     alloced = nil;
  209. }
  210.  
  211.     void
  212. ffree1(register const char *f)
  213. /* Free the block f, which was allocated by ftestalloc.  */
  214. {
  215.     register struct alloclist *p, **a = &alloced;
  216.  
  217.     while ((p = *a)->alloc  !=  f)
  218.         a = &p->nextalloc;
  219.     *a = p->nextalloc;
  220.     tfree(p->alloc);
  221.     tfree(p);
  222. }
  223.  
  224.     const char *
  225. strsave(const char *s)
  226. /* Save s in permanently allocated storage. */
  227. {
  228.     return strcpy(tnalloc(char, strlen(s)+1), s);
  229. }
  230.  
  231.     const char *
  232. fstrsave(const char *s)
  233. /* Save s in storage that will be deallocated when we're done with this file. */
  234. {
  235.     return strcpy(ftnalloc(char, strlen(s)+1), s);
  236. }
  237.  
  238.     char *
  239. cgetenv(const char *name)
  240. /* Like getenv(), but yield a copy; getenv() can overwrite old results. */
  241. {
  242.     register char *p;
  243.  
  244.     return (p=getenv(name)) ? (char *)strsave(p) : p;
  245. }
  246.  
  247.  
  248.     const char *
  249. getcaller()
  250. /* Function: gets the caller's login.
  251.  */
  252. {
  253.     static char *name;
  254.  
  255.     if (!name) {
  256.         if (!(
  257.             /* Use getenv() if we're trustworthy; it's much faster.  */
  258. #if SETID
  259.             euid==ruid && egid==rgid &&
  260. #endif
  261.             (
  262.                 (name = cgetenv("LOGNAME"))
  263.             ||    (name = cgetenv("USER"))
  264.             )
  265.  
  266. #ifdef POSIX
  267.             /* Follow a procedure recommended by Posix 1003.1-1988.  */
  268.             ||    (name = getlogin())
  269. #endif
  270.         )) {
  271. #if has_getuid & defined(declare_getpwuid)
  272.             const struct passwd *pw = getpwuid(ruid);
  273.             if (!pw)
  274.                 faterror("no password entry for userid %lu",
  275.                      (unsigned long)ruid
  276.                 );
  277.             name = pw->pw_name;
  278. #else
  279.             faterror("Who are you?  Please set LOGNAME.");
  280. #endif
  281.         }
  282.         checksid(name);
  283.     }
  284.     return name;
  285. }
  286.  
  287.  
  288.  
  289.     int
  290. findlock(int ldelete, struct hshentry **target)
  291. /* Finds the first lock held by caller and returns a pointer
  292.  * to the locked delta; also removes the lock if delete is set.
  293.  * Returns 0 for no locks, 1 for one, 2 for two or more.
  294.  * If one lock, puts it into *target.
  295.  */
  296. {
  297.     register struct lock *next, **trail, **found = nil;
  298.  
  299.     for (trail = &Locks;  (next = *trail);  trail = &next->nextlock)
  300.         if (strcmp(getcaller(), next->login)  ==  0) {
  301.             if (found) {
  302.                 error("multiple revisions locked by %s; please specify one", getcaller());
  303.                 return 2;
  304.             }
  305.             found = trail;
  306.         }
  307.     if (!found)
  308.         return 0;
  309.     next = *found;
  310.     *target = next->delta;
  311.     if (ldelete) {
  312.         next->delta->lockedby = nil;
  313.         *found = next->nextlock;
  314.     }
  315.     return 1;
  316. }
  317.  
  318.  
  319.  
  320.  
  321.  
  322.  
  323.  
  324.     int
  325. addlock(struct hshentry *delta)
  326. /* Add a lock held by caller to delta and yield 1 if successful.
  327.  * Print an error message and yield -1 if no lock is added because
  328.  * the delta is locked by somebody other than caller.
  329.  * Yield 0 if the caller already holds the lock.  */
  330. {
  331.         struct lock * next;
  332.  
  333.         next=Locks;
  334.         while (next!=nil) {
  335.                 if (cmpnum(delta->num,next->delta->num)==0) {
  336.             if (strcmp(getcaller(),next->login)==0)
  337.                 return 0;
  338.                         else {
  339.                                 error("revision %s already locked by %s",
  340.                                       delta->num, next->login);
  341.                 return -1;
  342.                         }
  343.                 }
  344.         next = next->nextlock;
  345.     }
  346.         /* set up new lockblock */
  347.     next = ftalloc(struct lock);
  348.     delta->lockedby=next->login=getcaller();
  349.         next->delta= delta;
  350.         next->nextlock=Locks;
  351.         Locks=next;
  352.     return 1;
  353. }
  354.  
  355.  
  356.  
  357.     int
  358. addsymbol(const char *num, const char *name, int rebind)
  359. /* Function: adds a new symbolic name and associates it with revision num.
  360.  * If name already exists and rebind is true, the name is associated
  361.  * with the new num; otherwise, an error message is printed and
  362.  * false returned. Returns true it successful.
  363.  */
  364. {       register struct assoc * next;
  365.         next=Symbols;
  366.         while (next!=nil) {
  367.                 if (strcmp(name,next->symbol)==0) {
  368.                         if (rebind) {
  369.                 next->num = num;
  370.                                 return true;
  371.                         } else {
  372.                                 error("symbolic name %s already bound to %s",
  373.                     name, next->num);
  374.                                 return false;
  375.                         }
  376.                 } else  next = next->nextassoc;
  377.         }
  378.         /* not found; insert new pair. */
  379.     next = ftalloc(struct assoc);
  380.         next->symbol=name;
  381.     next->num = num;
  382.         next->nextassoc=Symbols;
  383.         Symbols = next;
  384.         return true;
  385. }
  386.  
  387.  
  388.  
  389.  
  390. int checkaccesslist()
  391. /* function: Returns true if caller is the superuser, the owner of the
  392.  * file, the access list is empty, or caller is on the access list.
  393.  * Prints an error message and returns false otherwise.
  394.  */
  395. {
  396.     register const struct access *next;
  397.  
  398.     if (!AccessList || strcmp(getcaller(),"root")==0)
  399.                 return true;
  400.  
  401.         next=AccessList;
  402.         do {
  403.         if (strcmp(getcaller(),next->login)==0)
  404.                         return true;
  405.                 next=next->nextaccess;
  406.         } while (next!=nil);
  407.  
  408. #if has_getuid
  409.     {
  410.         struct stat statbuf;
  411.         VOID fstat(fileno(finptr),&statbuf);  /* get owner of file */
  412.         if (myself(statbuf.st_uid)) return true;
  413.     }
  414. #endif
  415.  
  416.     error("user %s not on the access list", getcaller());
  417.         return false;
  418. }
  419.  
  420.  
  421. /*
  422.  *     Signal handling
  423.  *
  424.  * ANSI C places too many restrictions on signal handlers.
  425.  * We obey as many of them as we can.
  426.  * Posix places fewer restrictions, and we are Posix-compatible here.
  427.  */
  428.  
  429. static volatile sig_atomic_t heldsignal, holdlevel;
  430.  
  431.     static signal_type
  432. catchsig(int s)
  433. {
  434.     const char *sname;
  435.     char buf[BUFSIZ];
  436.  
  437. #if sig_zaps_handler
  438.     /* If a signal arrives before we reset the signal handler, we lose. */
  439.     VOID signal(s, SIG_IGN);
  440. #endif
  441.     if (holdlevel) {
  442.         heldsignal = s;
  443.         return;
  444.     }
  445.     ignoreints();
  446.     setrid();
  447.     if (!quietflag) {
  448.         sname = nil;
  449. #if has_sys_siglist & defined(NSIG)
  450.         if ((unsigned)s < NSIG) {
  451. #        ifndef sys_siglist
  452.             extern const char *sys_siglist[];
  453. #        endif
  454.         sname = sys_siglist[s];
  455.         }
  456. #else
  457.         switch (s) {
  458. #ifdef SIGHUP
  459.         case SIGHUP:    sname = "Hangup";  break;
  460. #endif
  461. #ifdef SIGINT
  462.         case SIGINT:    sname = "Interrupt";  break;
  463. #endif
  464. #ifdef SIGPIPE
  465.         case SIGPIPE:    sname = "Broken pipe";  break;
  466. #endif
  467. #ifdef SIGQUIT
  468.         case SIGQUIT:    sname = "Quit";  break;
  469. #endif
  470. #ifdef SIGTERM
  471.         case SIGTERM:    sname = "Terminated";  break;
  472. #endif
  473. #ifdef SIGXCPU
  474.         case SIGXCPU:    sname = "Cputime limit exceeded";  break;
  475. #endif
  476. #ifdef SIGXFSZ
  477.         case SIGXFSZ:    sname = "Filesize limit exceeded";  break;
  478. #endif
  479.         }
  480. #endif
  481.         if (sname)
  482.         VOID sprintf(buf, "\nRCS: %s.  Cleaning up.\n", sname);
  483.         else
  484.         VOID sprintf(buf, "\nRCS: Signal %d.  Cleaning up.\n", s);
  485.         VOID write(STDERR_FILENO, buf, strlen(buf));
  486.     }
  487.     exiterr();
  488. }
  489.  
  490.     void
  491. ignoreints()
  492. {
  493.     ++holdlevel;
  494. }
  495.  
  496.     void
  497. restoreints()
  498. {
  499.     if (!--holdlevel && heldsignal)
  500.         VOID catchsig(heldsignal);
  501. }
  502.  
  503.  
  504. static const sig[] = {
  505. #ifdef SIGHUP
  506.     SIGHUP,
  507. #endif
  508. #ifdef SIGINT
  509.     SIGINT,
  510. #endif
  511. #ifdef SIGPIPE
  512.     SIGPIPE,
  513. #endif
  514. #ifdef SIGQUIT
  515.     SIGQUIT,
  516. #endif
  517. #ifdef SIGTERM
  518.     SIGTERM,
  519. #endif
  520. #ifdef SIGXCPU
  521.     SIGXCPU,
  522. #endif
  523. #ifdef SIGXFSZ
  524.     SIGXFSZ,
  525. #endif
  526. };
  527. #define SIGS (sizeof(sig)/sizeof(*sig))
  528.  
  529.  
  530. #if has_sigaction
  531.  
  532.     static void
  533.   checksig(r)
  534.     int r;
  535.   {
  536.     if (r < 0)
  537.         efaterror("signal");
  538.   }
  539.  
  540.     void
  541.   catchints()
  542.   {
  543.     register int i;
  544.     sigset_t blocked;
  545.     struct sigaction act;
  546.  
  547.     checksig(sigemptyset(&blocked));
  548.     for (i=SIGS; 0<=--i; )
  549.         checksig(sigaddset(&blocked, sig[i]));
  550.     for (i=SIGS; 0<=--i; ) {
  551.         checksig(sigaction(sig[i], (struct sigaction*)nil, &act));
  552.         if (act.sa_handler != SIG_IGN) {
  553.             act.sa_handler = catchsig;
  554.             act.sa_mask = blocked;
  555.             checksig(sigaction(sig[i], &act, (struct sigaction*)nil));
  556.         }
  557.     }
  558.   }
  559.  
  560. #else
  561. #if has_sigblock
  562.  
  563.   void catchints()
  564.   {
  565.     register int i;
  566.     int mask;
  567.  
  568.     mask = 0;
  569.     for (i=SIGS; 0<=--i; )
  570.         mask |= sigmask(sig[i]);
  571.     mask = sigblock(mask);
  572.     for (i=SIGS; 0<=--i; )
  573.         if (signal(sig[i], catchsig) == SIG_IGN)
  574.             VOID signal(sig[i], SIG_IGN);
  575.     VOID sigsetmask(mask);
  576.   }
  577.  
  578. #else
  579.  
  580.   void catchints()
  581.   {
  582.     register i;
  583.     for (i=SIGS; 0<=--i; )
  584.         if (signal(sig[i], SIG_IGN) != SIG_IGN)
  585.             VOID signal(sig[i], catchsig);
  586.   }
  587.  
  588. #endif
  589. #endif
  590.  
  591.  
  592.     void
  593. fastcopy(FILE *inf,FILE *outf)
  594. /* Function: copies the remainder of file inf to outf.
  595.  */
  596. {       char buf[BUFSIZ];
  597.     register fread_type rcount;
  598.  
  599.         /*now read the rest of the file in blocks*/
  600.     while (!feof(inf)  &&  (rcount = fread(buf,sizeof(char),BUFSIZ,inf))) {
  601.         awrite(buf, rcount, outf);
  602.         }
  603. }
  604.  
  605.     void
  606. awrite(const char *buf, fread_type chars, FILE *f)
  607. {
  608.     if (fwrite(buf, sizeof(char), chars, f) != chars)
  609.         IOerror();
  610. }
  611.  
  612.  
  613.  
  614.  
  615.  
  616. /*
  617. * Print RCS format date and time in user-readable format.
  618. */
  619.     void
  620. printdate(register FILE *f, const char *date, const char *separator)
  621. {
  622.     register const char *p = date;
  623.  
  624.     while (*p++ != '.')
  625.         ;
  626.     aprintf(f, "%s%.*s/%.2s/%.2s%s%.2s:%.2s:%s",
  627.         date[2]=='.' && VERSION(5)<=RCSversion  ?  "19"  :  "",
  628.         p-date-1, date,
  629.         p, p+3, separator, p+6, p+9, p+12
  630.     );
  631. }
  632.  
  633.  
  634.  
  635.  
  636. static int fdreopen(int fd, const char *file, int flags, mode_t mode)
  637. {
  638.     int newfd;
  639.     VOID close(fd);
  640.     newfd =
  641. #if !open_can_creat
  642.         flags&O_CREAT ? creat(file,mode) :
  643. #endif
  644.         open(file,flags,mode);
  645.     if (newfd < 0  ||  newfd == fd)
  646.         return newfd;
  647.     fd = dup2(newfd, fd);
  648.     VOID close(newfd);
  649.     return fd;
  650. }
  651.  
  652. static void tryopen(int fd,const char *file,int flags)
  653. {
  654.     if (file  &&  fdreopen(fd,file,flags,S_IRUSR|S_IWUSR) != fd) {
  655.         VOID write(STDERR_FILENO, file, strlen(file));
  656.         VOID write(STDERR_FILENO, ": can't open\n", 13);
  657.         _exit(EXIT_TROUBLE);
  658.     }
  659. }
  660.  
  661. /*
  662. * Run a command specified by the strings in 'inoutargs'.
  663. * inoutargs[0], if nonnil, is the name of the input file.
  664. * inoutargs[1], if nonnil, is the name of the output file.
  665. * inoutargs[2..] form the command to be run.
  666. */
  667.     int
  668. runv(const char **inoutargs)
  669. {
  670.     int pid;
  671.     int wstatus, w, old_stdin, old_stdout;
  672.     register const char **p;
  673.     oflush();
  674.     eflush();
  675.     old_stdin = dup(STDIN_FILENO);
  676.     old_stdout = dup(STDOUT_FILENO);
  677.     p = inoutargs;
  678.     tryopen(STDIN_FILENO, *p++, O_RDONLY);
  679.     tryopen(STDOUT_FILENO, *p++, O_CREAT|O_TRUNC|O_WRONLY);
  680.     wstatus = spawnvp( P_WAIT, (char *)*p, (char **)p);
  681.  
  682.     (void) dup2(old_stdin,STDIN_FILENO);    (void) close(old_stdin);
  683.     (void) dup2(old_stdout,STDOUT_FILENO);    (void) close(old_stdout);
  684. #ifdef NOTDEF
  685.     if (!(pid = vfork())) {
  686.         p = inoutargs;
  687.         tryopen(STDIN_FILENO, *p++, O_RDONLY);
  688.         tryopen(STDOUT_FILENO, *p++, O_CREAT|O_TRUNC|O_WRONLY);
  689.         VOID EXECRCS(*p, p);
  690.         if (errno == ENOEXEC) {
  691.             *--p = "/bin/sh";
  692.             VOID execv(*p, p);
  693.         }
  694.         VOID write(STDERR_FILENO, *p, strlen(*p));
  695.         VOID write(STDERR_FILENO, ": not found\n", 12);
  696.         _exit(EXIT_TROUBLE);
  697.     }
  698.     if (pid < 0)
  699.         return pid;
  700.     do {
  701.         if ((w = wait(&wstatus)) < 0)
  702.             return w;
  703.     } while (w != pid);
  704. #endif
  705.     return wstatus;
  706. }
  707.  
  708. #define CARGSMAX 20
  709. /*
  710. * Run a command.
  711. * The first two arguments are the input and output files (if nonnil);
  712. * the rest specify the command and its arguments.
  713. */
  714.     int
  715. #if has_prototypes
  716. run(const char *infile, const char *outfile, ...)
  717. #else
  718.     /*VARARGS2*/
  719. run(infile, outfile, va_alist)
  720.     const char *infile;
  721.     const char *outfile;
  722.     va_dcl
  723. #endif
  724. {
  725.     va_list ap;
  726.     const char *rgargs[CARGSMAX];
  727.     register i = 0;
  728.     rgargs[0] = infile;
  729.     rgargs[1] = outfile;
  730.     vararg_start(ap, outfile);
  731.     for (i = 2;  (rgargs[i++] = va_arg(ap, const char*));  )
  732.         if (CARGSMAX <= i)
  733.             faterror("too many command arguments");
  734.     va_end(ap);
  735.     return runv(rgargs);
  736. }
  737.  
  738.  
  739. int RCSversion;
  740.  
  741.     void
  742. setRCSversion(const char *str)
  743. {
  744.     static const char *oldversion;
  745.  
  746.     register const char *s = str + 2;
  747.     int v = VERSION_DEFAULT;
  748.  
  749.     if (oldversion)
  750.         redefined('V');
  751.     oldversion = str;
  752.  
  753.     if (*s) {
  754.         v = 0;
  755.         while (isdigit(*s))
  756.             v  =  10*v + *s++ - '0';
  757.         if (*s)
  758.             faterror("%s isn't a number", str);
  759.         if (v < VERSION_MIN  ||  VERSION_MAX < v)
  760.             faterror("%s out of range %d..%d", str, VERSION_MIN, VERSION_MAX);
  761.     }
  762.  
  763.     RCSversion = VERSION(v);
  764. }
  765.  
  766.     void
  767. initid()
  768. {
  769. #if SETID
  770.     egid = getegid();
  771.     euid = geteuid();
  772.     rgid = getgid();
  773. #endif
  774. #if has_getuid
  775.     ruid = getuid();
  776. #endif
  777.     setrid();
  778. }
  779.  
  780.  
  781. #if SETID
  782.     void
  783. seteid()
  784. /* Become effective user and group.  */
  785. {
  786.     if (euid!=ruid && seteuid(euid)<0  ||  egid!=rgid && setegid(egid)<0)
  787.         efaterror("seteid");
  788. }
  789.  
  790.     void
  791. setrid()
  792. /* Become real user and group.  */
  793. {
  794.     if (euid!=ruid && seteuid(ruid)<0  ||  egid!=rgid && setegid(rgid)<0)
  795.         efaterror("setrid");
  796. }
  797. #endif
  798.